// Copyright (C) joyinter
// All rights reserved.
// 
// Author : Jiang Li
// Date   : 2004-6-21

#include "stdafx.h"
#include "ctrl.h"

//##ModelId=44CF17B90382
class CJoyAutoTransform
{
protected:
	//##ModelId=44CF17B9038B
    Graphics& m_graph;
	//##ModelId=44CF17B9039A
    Matrix    m_trans;
public:
	//##ModelId=44CF17B903A9
    CJoyAutoTransform(Graphics& graph):
        m_graph(graph)
    {
        m_graph.GetTransform(&m_trans);
    }
	//##ModelId=44CF17B903B9
    ~CJoyAutoTransform()
    {
        m_graph.SetTransform(&m_trans);
    }
};

//##ModelId=44CF17B903BA
class CJoyAutoClip
{
protected:
	//##ModelId=44CF17B903C9
    Graphics& m_graph;
	//##ModelId=44CF17B903D9
    Region    m_clip;
public:
	//##ModelId=44CF17BA0000
    CJoyAutoClip(Graphics& graph):
        m_graph(graph)
    {
        m_graph.GetClip(&m_clip);
    }
	//##ModelId=44CF17BA0002
    ~CJoyAutoClip()
    {
        m_graph.SetClip(&m_clip);
    }
};

//##ModelId=44CF17B803B3
CJoyCtrl::CJoyCtrl(void)
{
    m_recvMouse = true;
    m_visible   = true;
    m_enabled   = true;
    m_hitTrans  = false;
    m_canActive = true;
    m_active    = false;

    m_skinArea  = false;
    m_skinAlign = false;

    m_parent    = NULL;
    m_clip      = NULL;
    m_align     = AT_None;

    m_hitTest   = 0;

    m_area.SetRect(0, 0, 1, 1);
    m_moveArea.SetRect(0, 0, 0, 0);
    m_leftDown.SetPoint(-1, -1);

    m_backColor.SetValue(0x00000000);
    m_textColor.SetValue(0xFF000000);

    m_textFace = JoyGetManager()->GetDefFontFace();
    m_textSize = JoyGetManager()->GetDefFontSize();
    m_lastLeftDown  = 0;
    m_doubleDelay   = 0;
}

//##ModelId=44CF17B803BA
CJoyCtrl::~CJoyCtrl(void)
{
    if(m_clip != NULL)
    {
        delete m_clip;
    }
}

//##ModelId=44CF17B803DA
void CJoyCtrl::SetClip(Region* clip)
{
    if(m_clip != NULL)
    {
        delete m_clip;
    }
    m_clip = clip;
}

//##ModelId=44CF17B9008F
void CJoyCtrl::SetEnabled(bool e)
{
    CM_Enabled(e);
}

//##ModelId=44CF17B90024
bool CJoyCtrl::Create(CJoyCtrl* parent, const CRect& area)
{
    m_area      = area;
    m_parent    = parent;
    if(!CM_Create())
    {
        CM_Destroy();
        return true;
    }
    return true;
}

//##ModelId=44CF17B802D0
bool CJoyCtrl::PointIn(const CPoint& p)
{
    CRect area(0, 0, m_area.Width(), m_area.Height());
    if(!area.PtInRect(p))
    {
        return false;
    }
    if(m_clip != NULL)
    {
        if(!m_clip->IsVisible(p.x, p.y))
        {
            return false;
        }
    }
    return true;
}

//##ModelId=44CF17B90000
void CJoyCtrl::SetBackColor(Color backColor)
{
    if(m_backColor.GetValue() != backColor.GetValue())
    {
        m_backColor = backColor;
        InvalidateCtrl();
    }
}

//##ModelId=44CF17B9007D
void CJoyCtrl::SetCtrlCapture(CJoyCtrl* pCtrl)
{
    if(m_parent != NULL)
    {
        m_parent->SetCtrlCapture(pCtrl);
    }
}

//##ModelId=44CF17B9008C
void CJoyCtrl::ReleaseCtrlCapture(CJoyCtrl* pCtrl)
{
    if(m_parent != NULL)
    {
        m_parent->ReleaseCtrlCapture(pCtrl);
    }
}

//##ModelId=44CF17B802FE
CJoyCtrl* CJoyCtrl::FindCtrl(const CPoint& pt, bool (*pfnIsValid)(CJoyCtrl*))
{
    POSITION pos = m_childs.GetTailPosition();
    while(pos != NULL)
    {
        CJoyCtrl* child = m_childs.GetPrev(pos);

        CPoint cpt(pt-child->m_area.TopLeft());

        //if(child->m_visible && child->m_recvMouse && child->PointIn(cpt))
        if(child->PointIn(cpt) && pfnIsValid(child))
        {
            return child->FindCtrl(cpt, pfnIsValid);
        }
    }
    return this;
}

//##ModelId=44CF17B802EE
void CJoyCtrl::RemoveChild(CJoyCtrl* child)
{
    m_childs.RemoveAt(GetChildPos(child));
    child->m_parent = NULL;

    if(child->m_align != AT_None)
    {
        AlignChilds();
    }
}

//##ModelId=44CF17B802DF
void CJoyCtrl::AddChild(CJoyCtrl* child)
{
    m_childs.AddTail(child);
    child->m_parent = this;

    if(child->m_align != AT_None)
    {
        AlignChilds();
    }
}

//##ModelId=44CF17B803CD
void CJoyCtrl::SetVisible(bool v)
{
    if(m_visible != v)
    {
        CM_Visible(v);
    }
}

//##ModelId=44CF17B9002E
void CJoyCtrl::Destroy()
{
    CM_Destroy();
}

//##ModelId=44CF17B9003E
void CJoyCtrl::Draw(Graphics& graph)
{
    if(!m_visible)
    {
        return;
    }

    Rect area;
    if(IsWnd())
    {
        area.X  = 0;
        area.Y  = 0;
    }
    else
    {
        area.X  = m_area.left;
        area.Y  = m_area.top;
    }
    area.Width  = m_area.Width();
    area.Height = m_area.Height();

    if(!graph.IsVisible(area))
    {
        return;
    }

    CJoyAutoTransform autoTrans(graph);
    graph.TranslateTransform((REAL)area.X, (REAL)area.Y, MatrixOrderPrepend);

    CJoyAutoClip autoClip(graph);    
    CM_Clip(graph);

    CM_Draw(graph);

    POSITION pos = m_childs.GetHeadPosition();
    while(pos != NULL)
    {
        CJoyCtrl* child = m_childs.GetNext(pos);
        child->Draw(graph);
    }
}

//##ModelId=44CF17B802C6
void CJoyCtrl::DoAlignChilds(CRect& remain)
{
    POSITION pos = m_childs.GetHeadPosition();
    while(pos)
    {
        CJoyCtrl* child = m_childs.GetNext(pos);
        if(!child->m_visible || child->m_align == AT_None)
        {
            continue;
        }
        CRect newRect, clientRect(child->m_area);
        switch(child->m_align)
        {
        case AT_Left:
            newRect.left    = remain.left;
            newRect.top     = remain.top;
            newRect.right   = newRect.left+clientRect.Width();
            newRect.bottom  = remain.bottom;

            remain.left     = newRect.right;
            break;
        case AT_Top:
            newRect.left    = remain.left;
            newRect.top     = remain.top;
            newRect.right   = remain.right;
            newRect.bottom  = newRect.top+clientRect.Height();

            remain.top      = newRect.bottom;
            break;
        case AT_Right:
            newRect.left    = remain.right-clientRect.Width();
            newRect.top     = remain.top;
            newRect.right   = remain.right;
            newRect.bottom  = remain.bottom;

            remain.right    = newRect.left;
            break;
        case AT_Bottom:
            newRect.left    = remain.left;
            newRect.top     = remain.bottom-clientRect.Height();
            newRect.right   = remain.right;
            newRect.bottom  = remain.bottom;

            remain.bottom   = newRect.top;
            break;
        case AT_Client:
            newRect = remain;
            remain.SetRect(0, 0, 0, 0);
            break;
        default:
            TRACE("Invalid align type.\n");
            break;
        }
        child->SetArea(newRect);
    }
}

//##ModelId=44CF17B90041
void CJoyCtrl::AlignChilds()
{
    //CRect remain(0, 0, m_area.Width(), m_area.Height());
    //DoAlignChilds(remain);
    DoAlignChilds();
}

//##ModelId=44CF17B9004E
void CJoyCtrl::SetArea(const CRect& area)
{
    if(m_area == area)
    {
        return;
    }
    if(!IsWnd())
    {
        InvalidateCtrl();
    }
    CM_Move(area.TopLeft());
    CRect s(area);
    CM_Sizing(0, s);
    CM_Size(s.Size());
}

//##ModelId=44CF17B90051
void CJoyCtrl::InvalidateCtrl(const CRect& area)
{
    //if(!m_visible)
    //{
    //    return;
    //}

    if(m_parent != NULL)
    {
        CRect a(
            area.left   + m_area.left,
            area.top    + m_area.top,
            area.right  + m_area.left,
            area.bottom + m_area.top);

        return m_parent->InvalidateCtrl(a);
    }
}

//##ModelId=44CF17B90060
void CJoyCtrl::CtrlToScreen(CPoint& pt)
{
    if(m_parent != NULL)
    {
        pt.x    += m_area.left;
        pt.y    += m_area.top;
        m_parent->CtrlToScreen(pt);
    }
}

//##ModelId=44CF17B9005D
void CJoyCtrl::CtrlToWnd(CPoint& pt)
{
    if(m_parent != NULL)
    {
        pt.x    += m_area.left;
        pt.y    += m_area.top;
        m_parent->CtrlToWnd(pt);
    }
}

//##ModelId=44CF17B9006D
void CJoyCtrl::SetAlign(AlignType align)
{
    if(align == m_align)
    {
        return;
    }
    m_align = align;
    if(m_parent != NULL)
    {
        m_parent->AlignChilds();
    }
}

//##ModelId=44CF17B90070
void CJoyCtrl::NotifyAlign()
{
    if(m_visible && m_parent != NULL)
    {
        m_parent->NotifyAlign();
    }
}

//##ModelId=44CF17B90022
bool CJoyCtrl::IsWnd()
{
    return false;
}

//##ModelId=44CF17B90072
void CJoyCtrl::LoadSkin(CJoyXmlElement* skinXml)
{
    POSITION pos = m_childs.GetHeadPosition();
    while(pos != NULL)
    {
        CJoyCtrl* child = m_childs.GetNext(pos);
        if(child->m_name.IsEmpty())
        {
            continue;
        }
        child->LoadSkin(skinXml->GetElement(child->m_name));
    }

    CM_LoadSkin(skinXml);
}

//##ModelId=44CF17B802C4
POSITION CJoyCtrl::GetChildPos(CJoyCtrl* child)
{
    POSITION pos = m_childs.GetHeadPosition();
    while(pos != NULL)
    {
        POSITION temp = pos;
        if(m_childs.GetNext(pos) == child)
        {
            return temp;
        }
    }
    return NULL;
}

//##ModelId=44CF17B8037A
void CJoyCtrl::CM_Active(bool active)
{
    m_active = active;
}

//##ModelId=44CF17B8036F
void CJoyCtrl::CM_Clip(Graphics& graph)
{
    Rect clipRect(0, 0, m_area.Width(), m_area.Height());
    graph.SetClip(clipRect, CombineModeIntersect);
    //graph.SetClip(Area(), CombineModeIntersect);
    if(m_clip != NULL)
    {
        graph.SetClip(m_clip, CombineModeIntersect);
    }
}

//##ModelId=44CF17B8036C
void CJoyCtrl::CM_Draw(Graphics& graph)
{
    FillBack(graph, m_backColor);
}

//##ModelId=44CF17B9000F
void CJoyCtrl::FillBack(Graphics& graph, Color c)
{
    if(c.GetValue() == 0)
    {
        return;
    }
    SolidBrush brush(c);
    graph.FillRectangle(&brush, 0, 0, m_area.Width(), m_area.Height());
}

//##ModelId=44CF17B8030D
LRESULT CJoyCtrl::CM_HitTest(const CPoint& )
{
    return m_hitTest;
}

//##ModelId=44CF17B80310
void CJoyCtrl::CM_MouseLeave()
{
    m_leftDown.x = -1;
    m_leftDown.y = -1;
    ReleaseCtrlCapture();
}

//##ModelId=44CF17B80312
void CJoyCtrl::CM_MouseHover()
{
    m_leftDown.x = -1;
    m_leftDown.y = -1;
    ReleaseCtrlCapture();
}

//##ModelId=44CF17B8031D
void CJoyCtrl::CM_Mouse(MouseMessage msg, const CPoint& pt)
{
    switch(msg)
    {
    case MM_LeftDown:
        if(m_doubleDelay > 0)
        {
            DWORD tick = GetTickCount();
            if(tick-m_lastLeftDown < m_doubleDelay)
            {
                CM_Mouse(MM_LeftDouble, pt);
            }
            m_lastLeftDown = tick;
        }
        if(m_moveArea.Width() == 0 && m_moveArea.Height() == 0)
        {
            break;
        }
        m_leftDown = pt;
        SetCtrlCapture();
        break;
    case MM_LeftUp:
    case MM_RightDown:
    case MM_RightUp:
        if(m_moveArea.Width() == 0 && m_moveArea.Height() == 0)
        {
            break;
        }
        m_leftDown.x = -1;
        m_leftDown.y = -1;
        ReleaseCtrlCapture();
        break;
    case MM_Move:
        if(m_leftDown.x < 0)
        {
            break;
        }
        if(m_moveArea.Width() == 0 && m_moveArea.Height() == 0)
        {
            break;
        }
        {
            CPoint diff(pt.x - m_leftDown.x, pt.y - m_leftDown.y);
            CRect newArea;
            CPoint newPos;
            newPos.x    = m_area.left + diff.x;
            newPos.y    = m_area.top + diff.y;

            if(newPos.x < m_moveArea.left)
            {
                newPos.x = m_moveArea.left;
            }
            else if(newPos.x > m_moveArea.right)
            {
                newPos.x = m_moveArea.right;
            }
            if(newPos.y < m_moveArea.top)
            {
                newPos.y = m_moveArea.top;
            }
            else if(newPos.y > m_moveArea.bottom)
            {
                newPos.y = m_moveArea.bottom;
            }
            CM_Drag(newPos);
        }
        break;
    }
}

//##ModelId=44CF17B8034E
void CJoyCtrl::CM_Drag(const CPoint& newPos)
{
    CRect newArea(
        newPos.x,
        newPos.y,
        newPos.x + m_area.Width(),
        newPos.y + m_area.Height());
    if(newArea != m_area)
    {
        SetArea(newArea);
    }
}

//##ModelId=44CF17B80321
void CJoyCtrl::CM_ParentEnabled(bool )
{
}

//##ModelId=44CF17B8032E
void CJoyCtrl::CM_Enabled(bool e)
{
    m_enabled = e;
    InvalidateCtrl();
}

//##ModelId=44CF17B80331
void CJoyCtrl::CM_Visible(bool v)
{
    m_visible = v;
    InvalidateCtrl();
    if(m_align != AT_None)
    {
        if(m_parent != NULL)
        {
            m_parent->AlignChilds();
        }
    }
}

//##ModelId=44CF17B8033C
void CJoyCtrl::CM_Sizing(UINT , CRect& )
{
}

//##ModelId=44CF17B80340
void CJoyCtrl::CM_Size(const CSize& s)
{
    m_area.right    = m_area.left + s.cx;
    m_area.bottom   = m_area.top + s.cy;

    InvalidateCtrl();

    DoAlignChilds();
    // AlignChilds();

    if(m_parent != NULL)
    {
        //m_parent->AlignChilds();
    }
}

//##ModelId=44CF17B8034B
void CJoyCtrl::CM_Move(const CPoint& p)
{
    int w = m_area.Width();
    int h = m_area.Height();
    m_area.SetRect(p.x, p.y, p.x+w, p.y+h);

    InvalidateCtrl();
}

//##ModelId=44CF17B8035B
void CJoyCtrl::CM_LoadSkin(CJoyXmlElement* pSkinElem)
{
    if(m_skinArea)
    {
        CRect area = pSkinElem->GetRect("area", m_area);
        SetArea(area);
    }
    if(m_skinAlign)
    {
        AlignType align = m_align;
        CString v = pSkinElem->ReadString("align");
        if(v == "left")
        {
            align = AT_Left;
        }
        else if(v == "top")
        {
            align = AT_Top;
        }
        else if(v == "right")
        {
            align = AT_Right;
        }
        else if(v == "bottom")
        {
            align = AT_Bottom;
        }
        else if(v == "client")
        {
            align = AT_Client;
        }
        else if(v == "none")
        {
            align = AT_None;
        }
        SetAlign(align);
    }
    m_backColor.SetValue(pSkinElem->ReadIntX("back_color", m_backColor.GetValue()));
    m_textColor.SetValue(pSkinElem->ReadIntX("text_color", m_textColor.GetValue()));

    CString textFace(m_textFace);
    //JoyConvertStr(textFace, m_textFace);
    textFace = pSkinElem->ReadString("text_face", textFace);
    m_textFace = CStringW(textFace);
    m_textSize = pSkinElem->ReadIntD("text_size", m_textSize);
}

//##ModelId=44CF17B8035E
bool CJoyCtrl::CM_Create()
{
    if(m_parent != NULL)
    {
        m_parent->AddChild(this);
    }
    return true;
}

//##ModelId=44CF17B80360
void CJoyCtrl::CM_Destroy()
{
    if(m_clip != NULL)
    {
        delete m_clip;
        m_clip = NULL;
    }

    POSITION pos = m_childs.GetHeadPosition();
    while(pos != NULL)
    {
        CJoyCtrl* child = m_childs.GetNext(pos);
        child->m_parent = NULL;
        child->Destroy();
        delete child;
    }

    if(m_parent != NULL)
    {
        m_parent->RemoveChild(this);
    }
    CJoyManager* pMan = JoyGetManager();
    if(pMan->m_captureCtrl == this)
    {
        pMan->m_captureCtrl = NULL;
    }
    if(pMan->m_hoveredCtrl == this)
    {
        pMan->m_hoveredCtrl = NULL;
    }
}

//##ModelId=44CF17B80362
void CJoyCtrl::CM_NotifyAlign()
{
}
